home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / dosrcss.zip / PARTIME.C < prev    next >
C/C++ Source or Header  |  1990-07-18  |  16KB  |  514 lines

  1. /*
  2.  * PARTIME        parse date/time string into a TM structure
  3.  *
  4.  * Usage:
  5.  *      #include "time.h"             -- expanded tm structure
  6.  *    char *str; struct tm *tp;
  7.  *    partime(str,tp);
  8.  * Returns:
  9.  *    0 if parsing failed
  10.  *    else time values in specified TM structure (unspecified values
  11.  *        set to TMNULL)
  12.  * Notes:
  13.  *    This code is quasi-public; it may be used freely in like software.
  14.  *    It is not to be sold, nor used in licensed software without
  15.  *    permission of the author.
  16.  *    For everyone's benefit, please report bugs and improvements!
  17.  *     Copyright 1980 by Ken Harrenstien, SRI International.
  18.  *    (ARPANET: KLH @ SRI)
  19.  */
  20.  
  21. /* Hacknotes:
  22.  *    If parsing changed so that no backup needed, could perhaps modify
  23.  *        to use a FILE input stream.  Need terminator, though.
  24.  *    Perhaps should return 0 on success, else a non-zero error val?
  25.  *    Flush AMPM from TM structure and handle locally within PARTIME,
  26.  *        like midnight/noon?
  27.  */
  28.  
  29. #ifndef lint
  30. static char rcsid[]="$Header: /site/tmp/dosrcs/src/RCS/partime.c,v 5.3 90/07/15 20:24:09 lfk Release $";
  31. #endif
  32.  
  33. /* $Log:    partime.c,v $
  34.  * Revision 5.3  90/07/15  20:24:09  lfk
  35.  * Most major fixes added between rev 5.1 and rev 5.5:
  36.  *     signals fixed so they work on MS-DOS
  37.  *     Added MKS arguments code so argv can be large
  38.  *     added code to handle slashes a'la Unix
  39.  *     added more file extensions to system from MS-DOS
  40.  * 
  41.  * Revision 5.2  90/07/15  11:32:07  ROOT_DOS
  42.  * DOS version of RCS 4.0 checked in for MODS
  43.  * by lfk@athena.mit.edu
  44.  * Also update to MSC 6.0
  45.  * 
  46.  * Revision 1.4  89/05/01  14:48:46  narten
  47.  * fixed #ifdef DEBUG construct
  48.  * 
  49.  * Revision 1.3  88/11/08  12:02:15  narten
  50.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  51.  * 
  52.  * Revision 1.3  88/08/28  14:53:40  eggert
  53.  * Remove unportable "#endif XXX"s.
  54.  * 
  55.  * Revision 1.2  87/03/27  14:21:53  jenkins
  56.  * Port to suns
  57.  * 
  58.  * Revision 1.1  84/01/23  14:50:07  kcs
  59.  * Initial revision
  60.  * 
  61.  * Revision 1.1  82/05/06  11:38:26  wft
  62.  * Initial revision
  63.  * 
  64.  */
  65.  
  66. #include <stdio.h>
  67. #include <ctype.h>
  68. #include "time.h"
  69.  
  70. #ifndef lint
  71. static char timeid[] = TIMEID;
  72. #endif
  73.  
  74. struct tmwent {
  75.     char *went;
  76.     long wval;    /* must be big enough to hold pointer or integer */
  77.     char wflgs;
  78.     char wtype;
  79. };
  80.     /* wflgs */
  81. #define TWSPEC 01    /* Word wants special processing */
  82. #define TWTIME 02    /* Word is a time value (absence implies date) */
  83. #define TWDST  04    /* Word is a DST-type timezone */
  84. #define TW1200 010    /* Word is NOON or MIDNIGHT (sigh) */
  85.  
  86. int pt12hack();
  87. int ptnoise();
  88. struct tmwent tmwords [] = {
  89.     {"january",      0, 0, TM_MON},
  90.     {"february",     1, 0, TM_MON},
  91.     {"march",        2, 0, TM_MON},
  92.     {"april",        3, 0, TM_MON},
  93.     {"may",          4, 0, TM_MON},
  94.     {"june",         5, 0, TM_MON},
  95.     {"july",         6, 0, TM_MON},
  96.     {"august",       7, 0, TM_MON},
  97.     {"september",    8, 0, TM_MON},
  98.     {"october",      9, 0, TM_MON},
  99.     {"november",     10, 0, TM_MON},
  100.     {"december",     11, 0, TM_MON},
  101.  
  102.     {"sunday",       0, 0, TM_WDAY},
  103.     {"monday",       1, 0, TM_WDAY},
  104.     {"tuesday",      2, 0, TM_WDAY},
  105.     {"wednesday",    3, 0, TM_WDAY},
  106.     {"thursday",     4, 0, TM_WDAY},
  107.     {"friday",       5, 0, TM_WDAY},
  108.     {"saturday",     6, 0, TM_WDAY},
  109.  
  110.     {"gmt",          0*60, TWTIME, TM_ZON},   /* Greenwich */
  111.     {"gst",          0*60, TWTIME, TM_ZON},
  112.     {"gdt",          0*60, TWTIME+TWDST, TM_ZON},     /* ?? */
  113.  
  114.     {"ast",          4*60, TWTIME, TM_ZON},   /* Atlantic */
  115.     {"est",          5*60, TWTIME, TM_ZON},   /* Eastern */
  116.     {"cst",          6*60, TWTIME, TM_ZON},   /* Central */
  117.     {"mst",          7*60, TWTIME, TM_ZON},   /* Mountain */
  118.     {"pst",          8*60, TWTIME, TM_ZON},   /* Pacific */
  119.     {"yst",          9*60, TWTIME, TM_ZON},   /* Yukon */
  120.     {"hst",          10*60, TWTIME, TM_ZON},  /* Hawaii */
  121.     {"bst",          11*60, TWTIME, TM_ZON},  /* Bering */
  122.  
  123.     {"adt",          4*60, TWTIME+TWDST, TM_ZON},     /* Atlantic */
  124.     {"edt",          5*60, TWTIME+TWDST, TM_ZON},     /* Eastern */
  125.     {"cdt",          6*60, TWTIME+TWDST, TM_ZON},     /* Central */
  126.     {"mdt",          7*60, TWTIME+TWDST, TM_ZON},     /* Mountain */
  127.     {"pdt",          8*60, TWTIME+TWDST, TM_ZON},     /* Pacific */
  128.     {"ydt",          9*60, TWTIME+TWDST, TM_ZON},     /* Yukon */
  129.     {"hdt",          10*60, TWTIME+TWDST, TM_ZON},    /* Hawaii */
  130.     {"bdt",          11*60, TWTIME+TWDST, TM_ZON},    /* Bering */
  131.  
  132.     {"daylight",     1, TWTIME+TWDST, TM_ZON},        /* Local Daylight */
  133.     {"standard",     1, TWTIME, TM_ZON},      /* Local Standard */
  134.     {"std",          1, TWTIME, TM_ZON},      /*   "       "    */
  135.  
  136.     {"am",           1, TWTIME, TM_AMPM},
  137.     {"pm",           2, TWTIME, TM_AMPM},
  138.     {"noon",         12,TWTIME+TW1200, 0},    /* Special frobs */
  139.     {"midnight",     0, TWTIME+TW1200, 0},
  140.     {"at",           (long)ptnoise, TWSPEC, 0},    /* Noise word */
  141.  
  142.     {0, 0, 0, 0},             /* Zero entry to terminate searches */
  143. };
  144.  
  145. #define TMWILD (-2)    /* Value meaning item specified as wild-card */
  146.             /* (May use someday...) */
  147.  
  148. struct token {
  149.     char *tcp;    /* pointer to string */
  150.     int tcnt;    /* # chars */
  151.     char tbrk;    /* "break" char */
  152.     char tbrkl;    /* last break char */
  153.     char tflg;    /* 0 = alpha, 1 = numeric */
  154.     union {         /* Resulting value; */
  155.         int tnum;/* either a #, or */
  156.         struct tmwent *ttmw;/* ptr to a tmwent. */
  157.     } tval;
  158. };
  159.  
  160. partime(astr, atm)
  161. char *astr;
  162. struct tm *atm;
  163. {    register int *tp;
  164.     register struct tmwent *twp;
  165.     register int i;
  166.     struct token btoken, atoken;
  167.     char *cp, ch;
  168.     int ord, midnoon;
  169.     int (*aproc)();
  170.  
  171.     tp = (int *)atm;
  172.     zaptime(tp);             /* Initialize the TM structure */
  173.     midnoon = TMNULL;        /* and our own temp stuff */
  174.     btoken.tcnt = btoken.tbrkl = 0;
  175.     btoken.tcp = astr;
  176.  
  177. domore:
  178.     if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))    /* Get a token */
  179.       {     if(btoken.tval.tnum) return(0);         /* Read error? */
  180.         if(midnoon != TMNULL)            /* EOF, wrap up */
  181.             return(pt12hack(tp, midnoon));
  182.         return(1);                /* Win return! */
  183.       }
  184.     if(btoken.tflg == 0)        /* Alpha? */
  185.       {     twp = btoken.tval.ttmw;         /* Yes, get ptr to entry */
  186.         if(twp->wflgs&TWSPEC)        /* Special alpha crock */
  187.           {     aproc = (int (*) ()) (twp->wval);
  188.             if(!(*aproc)(tp, twp, &btoken))
  189.                 return(0);    /* ERR: special word err */
  190.             goto domore;
  191.           }
  192.         if(twp->wflgs&TW1200)
  193.             if(ptstash(&midnoon,(int)twp->wval))
  194.                 return(0);    /* ERR: noon/midnite clash */
  195.             else goto domore;
  196.         if(ptstash(&tp[twp->wtype],(int)twp->wval))
  197.             return(0);        /* ERR: val already set */
  198.         if(twp->wtype == TM_ZON)    /* If was zone, hack DST */
  199.             if(ptstash(&tp[TM_ISDST],(twp->wflgs&TWDST)))
  200.                 return(0);    /* ERR: DST conflict */
  201.         goto domore;
  202.       }
  203.  
  204.     /* Token is number.  Lots of hairy heuristics. */
  205.     if(btoken.tcnt >= 7)    /* More than 6 digits in string? */
  206.         return(0);    /* ERR: number too big */
  207.     if(btoken.tcnt == 6)    /* 6 digits = HHMMSS.  Needs special crock */
  208.       {            /* since 6 digits are too big for integer! */
  209.         i = (btoken.tcp[0]-'0')*10    /* Gobble 1st 2 digits */
  210.            + btoken.tcp[1]-'0';
  211.         btoken.tcnt = 2;        /* re-read last 4 chars */
  212.         goto coltime;
  213.       }
  214.  
  215.     i = btoken.tval.tnum;   /* Value now known to be valid; get it. */
  216.     if( btoken.tcnt == 5    /*  5 digits = HMMSS */
  217.      || btoken.tcnt == 3)    /*  3 digits = HMM   */
  218.       {    if(btoken.tcnt != 3)
  219.             if(ptstash(&tp[TM_SEC], i%100))
  220.                 return(0);    /* ERR: sec conflict */
  221.             else i /= 100;
  222. hhmm4:        if(ptstash(&tp[TM_MIN], i%100))
  223.             return(0);        /* ERR: min conflict */
  224.         i /= 100;
  225. hh2:            if(ptstash(&tp[TM_HOUR], i))
  226.             return(0);        /* ERR: hour conflict */
  227.         goto domore;
  228.       }
  229.  
  230.     if(btoken.tcnt == 4)    /* 4 digits = YEAR or HHMM */
  231.       {    if(tp[TM_YEAR] != TMNULL) goto hhmm4;    /* Already got yr? */
  232.         if(tp[TM_HOUR] != TMNULL) goto year4;    /* Already got hr? */
  233.         if((i%100) > 59) goto year4;        /* MM >= 60? */
  234.         if(btoken.tbrk == ':')            /* HHMM:SS ? */
  235.             if( ptstash(&tp[TM_HOUR],i/100)
  236.              || ptstash(&tp[TM_MIN], i%100))
  237.                 return(0);        /* ERR: hr/min clash */
  238.             else goto coltm2;        /* Go handle SS */
  239.         if(btoken.tbrk != ',' && btoken.tbrk != '/'
  240.           && ptitoken(btoken.tcp+btoken.tcnt,&atoken)    /* Peek */
  241.           && atoken.tflg == 0            /* alpha */
  242.           && (atoken.tval.ttmw->wflgs&TWTIME))  /* HHMM-ZON */
  243.             goto hhmm4;
  244.         if(btoken.tbrkl == '-'        /* DD-Mon-YYYY */
  245.           || btoken.tbrkl == ','    /* Mon DD, YYYY */
  246.           || btoken.tbrkl == '/'    /* MM/DD/YYYY */
  247.           || btoken.tbrkl == '.'    /* DD.MM.YYYY */
  248.           || btoken.tbrk == '-'        /* YYYY-MM-DD */
  249.             ) goto year4;
  250.         goto hhmm4;            /* Give up, assume HHMM. */
  251.       }
  252.  
  253.     /* From this point on, assume tcnt == 1 or 2 */
  254.     /* 2 digits = YY, MM, DD, or HH (MM and SS caught at coltime) */
  255.     if(btoken.tbrk == ':')        /* HH:MM[:SS] */
  256.         goto coltime;        /*  must be part of time. */
  257.     if(i > 31) goto yy2;        /* If >= 32, only YY poss. */
  258.  
  259.     /* Check for numerical-format date */
  260.     for (cp = "/-."; ch = *cp++;)
  261.       {    ord = (ch == '.' ? 0 : 1);    /* n/m = D/M or M/D */
  262.         if(btoken.tbrk == ch)            /* "NN-" */
  263.           {    if(btoken.tbrkl != ch)
  264.               {    if(ptitoken(btoken.tcp+btoken.tcnt,&atoken)
  265.                   && atoken.tflg == 0
  266.                   && atoken.tval.ttmw->wtype == TM_MON)
  267.                     goto dd2;
  268.                 if(ord)goto mm2; else goto dd2; /* "NN-" */
  269.               }                /* "-NN-" */
  270.             if(tp[TM_DAY] == TMNULL
  271.             && tp[TM_YEAR] != TMNULL)    /* If "YY-NN-" */
  272.                 goto mm2;        /* then always MM */
  273.             if(ord)goto dd2; else goto mm2;
  274.           }
  275.         if(btoken.tbrkl == ch            /* "-NN" */
  276.           && tp[ord ? TM_MON : TM_DAY] != TMNULL)
  277.             if(tp[ord ? TM_DAY : TM_MON] == TMNULL)    /* MM/DD */
  278.                 if(ord)goto dd2; else goto mm2;
  279.             else goto yy2;            /* "-YY" */
  280.       }
  281.  
  282.     /* At this point only YY, DD, and HH are left.
  283.      * YY is very unlikely since value is <= 32 and there was
  284.      * no numerical format date.  Make one last try at YY
  285.      * before dropping through to DD vs HH code.
  286.      */
  287.     if(btoken.tcnt == 2        /* If 2 digits */
  288.       && tp[TM_HOUR] != TMNULL    /* and already have hour */
  289.       && tp[TM_DAY] != TMNULL    /* and day, but  */
  290.       && tp[TM_YEAR] == TMNULL)    /* no year, then assume */
  291.         goto yy2;        /* that's what we have. */
  292.  
  293.     /* Now reduced to choice between HH and DD */
  294.     if(tp[TM_HOUR] != TMNULL) goto dd2;    /* Have hour? Assume day. */
  295.     if(tp[TM_DAY] != TMNULL) goto hh2;    /* Have day? Assume hour. */
  296.     if(i > 24) goto dd2;            /* Impossible HH means DD */
  297.     if(!ptitoken(btoken.tcp+btoken.tcnt, &atoken))    /* Read ahead! */
  298.         if(atoken.tval.tnum) return(0); /* ERR: bad token */
  299.         else goto dd2;            /* EOF, assume day. */
  300.     if( atoken.tflg == 0        /* If next token is an alpha */
  301.      && atoken.tval.ttmw->wflgs&TWTIME)  /* time-spec, assume hour */
  302.         goto hh2;        /* e.g. "3 PM", "11-EDT"  */
  303.  
  304. dd2:    if(ptstash(&tp[TM_DAY],i))    /* Store day (1 based) */
  305.         return(0);
  306.     goto domore;
  307.  
  308. mm2:    if(ptstash(&tp[TM_MON], i-1))    /* Store month (make zero based) */
  309.         return(0);
  310.     goto domore;
  311.  
  312. yy2:    i += 1900;
  313. year4:    if(ptstash(&tp[TM_YEAR],i))    /* Store year (full number) */
  314.         return(0);        /* ERR: year conflict */
  315.     goto domore;
  316.  
  317.     /* Hack HH:MM[[:]SS] */
  318. coltime:
  319.     if(ptstash(&tp[TM_HOUR],i)) return(0);
  320.     if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
  321.         return(!btoken.tval.tnum);
  322.     if(!btoken.tflg) return(0);    /* ERR: HH:<alpha> */
  323.     if(btoken.tcnt == 4)        /* MMSS */
  324.         if(ptstash(&tp[TM_MIN],btoken.tval.tnum/100)
  325.           || ptstash(&tp[TM_SEC],btoken.tval.tnum%100))
  326.             return(0);
  327.         else goto domore;
  328.     if(btoken.tcnt != 2
  329.       || ptstash(&tp[TM_MIN],btoken.tval.tnum))
  330.         return(0);        /* ERR: MM bad */
  331.     if(btoken.tbrk != ':') goto domore;    /* Seconds follow? */
  332. coltm2:    if(!ptitoken(btoken.tcp+btoken.tcnt,&btoken))
  333.         return(!btoken.tval.tnum);
  334.     if(!btoken.tflg || btoken.tcnt != 2    /* Verify SS */
  335.       || ptstash(&tp[TM_SEC], btoken.tval.tnum))
  336.         return(0);        /* ERR: SS bad */
  337.     goto domore;
  338. }
  339.  
  340. /* Store date/time value, return 0 if successful.
  341.  * Fails if entry already set to a different value.
  342.  */
  343. ptstash(adr,val)
  344. int *adr;
  345. {    register int *a;
  346.     if( *(a=adr) != TMNULL)
  347.         return(*a != val);
  348.     *a = val;
  349.     return(0);
  350. }
  351.  
  352. /* This subroutine is invoked for NOON or MIDNIGHT when wrapping up
  353.  * just prior to returning from partime.
  354.  */
  355. pt12hack(atp, aval)
  356. int *atp, aval;
  357. {    register int *tp, i, h;
  358.     tp = atp;
  359.     if (((i=tp[TM_MIN]) && i != TMNULL)    /* Ensure mins, secs */
  360.      || ((i=tp[TM_SEC]) && i != TMNULL))    /* are 0 or unspec'd */
  361.         return(0);            /* ERR: MM:SS not 00:00 */
  362.     i = aval;            /* Get 0 or 12 (midnite or noon) */
  363.     if ((h = tp[TM_HOUR]) == TMNULL    /* If hour unspec'd, win */
  364.      || h == 12)            /* or if 12:00 (matches either) */
  365.         tp[TM_HOUR] = i;    /* Then set time */
  366.     else if(!(i == 0        /* Nope, but if midnight and */
  367.         &&(h == 0 || h == 24)))    /* time matches, can pass. */
  368.             return(0);    /* ERR: HH conflicts */
  369.     tp[TM_AMPM] = TMNULL;        /* Always reset this value if won */
  370.     return(1);
  371. }
  372.  
  373. /* Null routine for no-op tokens */
  374.  
  375. ptnoise() { return(1); }
  376.  
  377. /* Get a token and identify it to some degree.
  378.  * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
  379.  * hit error of some sort
  380.  */
  381.  
  382. ptitoken(astr, tkp)
  383. register struct token *tkp;
  384. char *astr;
  385. {
  386.     register char *cp;
  387.     register int i;
  388.  
  389.     tkp->tval.tnum = 0;
  390.     if(pttoken(astr,tkp) == 0)
  391. #ifdef DEBUG
  392.         {
  393.         VOID printf("EOF\n");
  394.         return(0);
  395.         }
  396. #else
  397.         return(0);
  398. #endif    
  399.     cp = tkp->tcp;
  400.  
  401. #ifdef DEBUG
  402.     i = cp[tkp->tcnt];
  403.     cp[tkp->tcnt] = 0;
  404.     VOID printf("Token: \"%s\" ",cp);
  405.     cp[tkp->tcnt] = i;
  406. #endif
  407.  
  408.     if(tkp->tflg)
  409.         for(i = tkp->tcnt; i > 0; i--)
  410.             tkp->tval.tnum = (int)tkp->tval.tnum*10 + ((*cp++)-'0');
  411.     else
  412.       {     i = ptmatchstr(cp, tkp->tcnt, tmwords);
  413.         tkp->tval.tnum = i ? i : -1;         /* Set -1 for error */
  414.  
  415. #ifdef DEBUG
  416.         if(!i) VOID printf("Not found!\n");
  417. #endif
  418.  
  419.         if(!i) return(0);
  420.       }
  421.  
  422. #ifdef DEBUG
  423.     if(tkp->tflg)
  424.         VOID printf("Val: %d.\n",tkp->tval.tnum);
  425.     else VOID printf("Found: \"%s\", val: %d., type %d\n",
  426.         tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
  427. #endif
  428.  
  429.     return(1);
  430. }
  431.  
  432. /* Read token from input string into token structure */
  433. pttoken(astr,tkp)
  434. register struct token *tkp;
  435. char *astr;
  436. {
  437.     register char *cp;
  438.     register int c;
  439.  
  440.     tkp->tcp = cp = astr;
  441.     tkp->tbrkl = tkp->tbrk;        /* Set "last break" */
  442.     tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
  443.  
  444.     while(c = *cp++)
  445.       {    switch(c)
  446.           {    case ' ': case '\t':    /* Flush all whitespace */
  447.                 while((c = *cp++) && isspace(c));
  448.                 cp--;        /* Drop thru to handle brk */
  449.             case '(': case ')':    /* Perhaps any non-alphanum */
  450.             case '-': case ',':    /* shd qualify as break? */
  451.             case '/': case ':': case '.':    /* Break chars */
  452.                 if(tkp->tcnt == 0)    /* If no token yet */
  453.                   {    tkp->tcp = cp;    /* ignore the brk */
  454.                     tkp->tbrkl = c;
  455.                       continue;    /* and go on. */
  456.                   }
  457.                 tkp->tbrk = c;
  458.                 return(tkp->tcnt);
  459.           }
  460.         if(tkp->tcnt == 0)        /* If first char of token, */
  461.             tkp->tflg = isdigit(c);    /*    determine type */
  462.           if(( isdigit(c) &&  tkp->tflg)    /* If not first, make sure */
  463.          ||(!isdigit(c) && !tkp->tflg))    /*    char matches type */
  464.             tkp->tcnt++;        /* Win, add to token. */
  465.         else {
  466.             cp--;            /* Wrong type, back up */
  467.             tkp->tbrk = c;
  468.             return(tkp->tcnt);
  469.           }
  470.       }
  471.     return(tkp->tcnt);        /* When hit EOF */
  472. }
  473.  
  474.  
  475. ptmatchstr(astr,cnt,astruc)
  476. char *astr;
  477. int cnt;
  478. struct tmwent *astruc;
  479. {    register char *cp, *mp;
  480.     register int c;
  481.     struct tmwent *lastptr;
  482.     struct integ { int word; };   /* For getting at array ptr */
  483.     int i;
  484.  
  485.     lastptr = 0;
  486.     for(;mp = (char *)((struct integ *)astruc)->word; astruc += 1)
  487.       {    cp = astr;
  488.         for(i = cnt; i > 0; i--)
  489.           {    switch((c = *cp++) ^ *mp++)    /* XOR the chars */
  490.               {    case 0: continue;    /* Exact match */
  491.                 case 040: if(isalpha(c))
  492.                     continue;
  493.               }
  494.             break;
  495.           }
  496.         if(i==0)
  497.             if(*mp == 0) return((unsigned int)astruc);    /* Exact match */
  498.             else if(lastptr) return(0);    /* Ambiguous */
  499.             else lastptr = astruc;        /* 1st ambig */
  500.       }
  501.     return((unsigned int)lastptr);
  502. }
  503.  
  504.  
  505.  
  506. zaptime(tp)
  507. register int *tp;
  508. /* clears tm structure pointed to by tp */
  509. {    register int i;
  510.     i = (sizeof (struct tm))/(sizeof (int));
  511.     do *tp++ = TMNULL;        /* Set entry to "unspecified" */
  512.     while(--i);            /* Faster than FOR */
  513. }
  514.